<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>WebRTC 完整信令演示 - NAT穿透测试</title>
    <style>
        * { box-sizing: border-box; }
        body { 
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; 
            margin: 0; 
            padding: 20px; 
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
        }
        .container { 
            max-width: 1200px; 
            margin: 0 auto; 
            background: white; 
            padding: 30px; 
            border-radius: 15px; 
            box-shadow: 0 10px 30px rgba(0,0,0,0.2);
        }
        .header { 
            text-align: center; 
            margin-bottom: 30px; 
            border-bottom: 3px solid #667eea; 
            padding-bottom: 20px;
        }
        .header h1 { 
            color: #333; 
            margin: 0; 
            font-size: 2.5em;
        }
        .header p { 
            color: #666; 
            font-size: 1.2em; 
            margin: 10px 0 0 0;
        }
        .panel { 
            background: #f8f9fa; 
            padding: 20px; 
            margin: 15px 0; 
            border-radius: 10px; 
            border-left: 5px solid #007bff;
        }
        .panel.success { border-left-color: #28a745; background: #f0fff4; }
        .panel.warning { border-left-color: #ffc107; background: #fffbf0; }
        .panel.info { border-left-color: #17a2b8; background: #f0fdff; }
        .btn { 
            padding: 12px 20px; 
            margin: 5px; 
            cursor: pointer; 
            border: none; 
            border-radius: 8px; 
            font-weight: bold; 
            font-size: 14px;
            transition: all 0.3s ease;
        }
        .btn:hover { transform: translateY(-2px); box-shadow: 0 5px 15px rgba(0,0,0,0.2); }
        .btn-connect { background: #007bff; color: white; }
        .btn-offer { background: #ffc107; color: black; }
        .btn-answer { background: #17a2b8; color: white; }
        .btn-success { background: #28a745; color: white; }
        .btn-danger { background: #dc3545; color: white; }
        .btn:disabled { background: #6c757d; cursor: not-allowed; transform: none; box-shadow: none; }
        .status { 
            padding: 15px; 
            margin: 15px 0; 
            border-radius: 8px; 
            font-weight: bold;
        }
        .connected { background: #d4edda; border: 2px solid #c3e6cb; color: #155724; }
        .disconnected { background: #f8d7da; border: 2px solid #f5c6cb; color: #721c24; }
        .processing { background: #fff3cd; border: 2px solid #ffeaa7; color: #856404; }
        .messages { 
            height: 250px; 
            border: 2px solid #e9ecef; 
            padding: 15px; 
            overflow-y: auto; 
            margin: 15px 0; 
            background: #fafafa; 
            font-family: 'Courier New', monospace; 
            font-size: 13px;
            border-radius: 8px;
        }
        .message-input { 
            width: 100%; 
            max-width: 400px;
            padding: 12px; 
            margin-right: 10px; 
            border: 2px solid #ddd; 
            border-radius: 8px; 
            font-size: 14px;
        }
        .grid-2 { 
            display: grid; 
            grid-template-columns: 1fr 1fr; 
            gap: 20px; 
            margin: 20px 0;
        }
        .grid-3 { 
            display: grid; 
            grid-template-columns: 1fr 1fr 1fr; 
            gap: 15px; 
            margin: 15px 0;
        }
        .info-item { 
            padding: 12px; 
            background: white; 
            border-radius: 8px; 
            border: 1px solid #e9ecef;
            text-align: center;
        }
        .info-item .label { 
            font-weight: bold; 
            color: #495057; 
            display: block; 
            margin-bottom: 5px;
        }
        .info-item .value { 
            color: #007bff; 
            font-family: 'Courier New', monospace;
            font-size: 14px;
        }
        .candidate-details { 
            background: #e7f3ff; 
            padding: 10px; 
            margin: 5px 0; 
            border-radius: 5px; 
            border-left: 3px solid #007bff;
        }
        .test-step { 
            background: white; 
            padding: 15px; 
            margin: 10px 0; 
            border-radius: 8px; 
            border: 2px solid #e9ecef;
        }
        .test-step h4 { margin: 0 0 10px 0; color: #495057; }
        .progress-bar { 
            height: 8px; 
            background: #e9ecef; 
            border-radius: 4px; 
            margin: 10px 0; 
            overflow: hidden;
        }
        .progress { 
            height: 100%; 
            background: linear-gradient(90deg, #007bff, #00c9ff); 
            width: 0%; 
            transition: width 0.5s ease;
        }
        .log-timestamp { color: #6c757d; font-size: 0.9em; }
        .log-signaling { color: #e83e8c; }
        .log-data { color: #20c997; }
        .log-ice { color: #6f42c1; }
        .log-error { color: #dc3545; font-weight: bold; }
    </style>
</head>
<body>
    <div class="container">
        <div class="header">
            <h1>🔗 WebRTC 完整信令演示</h1>
            <p>NAT穿透测试 & P2P连接建立全过程</p>
        </div>

        <!-- 测试进度 -->
        <div class="panel info">
            <h3>🧪 测试进度</h3>
            <div class="progress-bar">
                <div class="progress" id="testProgress"></div>
            </div>
            <div class="grid-3">
                <div class="info-item">
                    <span class="label">阶段 1</span>
                    <span class="value" id="stage1">待开始</span>
                </div>
                <div class="info-item">
                    <span class="label">阶段 2</span>
                    <span class="value" id="stage2">待开始</span>
                </div>
                <div class="info-item">
                    <span class="label">阶段 3</span>
                    <span class="value" id="stage3">待开始</span>
                </div>
            </div>
        </div>

        <div class="grid-2">
            <!-- 左侧：控制面板 -->
            <div>
                <!-- 阶段1：信令服务器连接 -->
                <div class="panel">
                    <h3>1. 连接信令服务器 (端口 8080)</h3>
                    <button id="connectBtn" class="btn btn-connect">连接信令服务器</button>
                    <button id="disconnectBtn" class="btn btn-danger" disabled>断开信令服务器</button>
                    <div id="connectionStatus" class="status disconnected">
                        <strong>信令状态:</strong> 未连接
                    </div>
                </div>

                <!-- 阶段2：P2P连接建立 -->
                <div class="panel">
                    <h3>2. 创建WebRTC P2P连接</h3>
                    <button id="createOfferBtn" class="btn btn-offer" disabled>创建Offer（Ubuntu端）</button>
                    <button id="startAnswerBtn" class="btn btn-answer" disabled>准备Answer（Windows端）</button>
                    <button id="closePeerBtn" class="btn btn-danger" disabled>关闭P2P连接</button>
                </div>

                <!-- 阶段3：P2P通信测试 -->
                <div class="panel success">
                    <h3>3. 测试P2P数据通道</h3>
                    <input type="text" id="messageInput" class="message-input" placeholder="输入要发送的消息" disabled>
                    <button id="sendMsgBtn" class="btn btn-success" disabled>发送消息</button>
                    <button id="sendPingBtn" class="btn btn-success" disabled>发送Ping</button>
                    <button id="testP2PBtn" class="btn btn-offer" disabled>测试P2P断开信令</button>
                </div>
            </div>

            <!-- 右侧：连接信息 -->
            <div>
                <div class="panel info">
                    <h3>🔗 连接状态信息</h3>
                    <div class="grid-2">
                        <div class="info-item">
                            <span class="label">ICE状态</span>
                            <span class="value" id="iceState">-</span>
                        </div>
                        <div class="info-item">
                            <span class="label">信令状态</span>
                            <span class="value" id="signalingState">-</span>
                        </div>
                        <div class="info-item">
                            <span class="label">连接状态</span>
                            <span class="value" id="connectionState">-</span>
                        </div>
                        <div class="info-item">
                            <span class="label">数据通道</span>
                            <span class="value" id="dataChannelState">未连接</span>
                        </div>
                    </div>
                </div>

                <div class="panel warning">
                    <h3>🌐 NAT穿透信息</h3>
                    <div class="info-item">
                        <span class="label">本地候选</span>
                        <span class="value" id="localCandidate">-</span>
                    </div>
                    <div class="info-item">
                        <span class="label">远程候选</span>
                        <span class="value" id="remoteCandidate">-</span>
                    </div>
                    <div id="candidateDetails"></div>
                </div>

                <div class="panel">
                    <h3>📊 统计信息</h3>
                    <div class="grid-2">
                        <div class="info-item">
                            <span class="label">消息发送</span>
                            <span class="value" id="sentCount">0</span>
                        </div>
                        <div class="info-item">
                            <span class="label">消息接收</span>
                            <span class="value" id="receivedCount">0</span>
                        </div>
                    </div>
                </div>
            </div>
        </div>

        <div class="grid-2">
            <div>
                <h3>📨 信令消息流 (通过服务器 8080)</h3>
                <div id="signalingMessages" class="messages"></div>
            </div>
            <div>
                <h3>📝 数据通道消息 (P2P直连)</h3>
                <div id="dataMessages" class="messages"></div>
            </div>
        </div>

        <!-- 详细测试步骤 -->
        <div class="panel">
            <h3>🔍 详细测试步骤说明</h3>
            <div class="test-step">
                <h4>步骤 1: 连接信令服务器</h4>
                <p>两台设备都点击"连接信令服务器"，建立与信令服务器的WebSocket连接</p>
            </div>
            <div class="test-step">
                <h4>步骤 2: 交换信令信息</h4>
                <p>Ubuntu端点击"创建Offer"，Windows端点击"准备Answer"，通过信令服务器交换SDP信息</p>
            </div>
            <div class="test-step">
                <h4>步骤 3: ICE候选交换</h4>
                <p>观察ICE候选的收集和交换过程，注意NAT穿透产生的公网地址</p>
            </div>
            <div class="test-step">
                <h4>步骤 4: P2P连接建立</h4>
                <p>当ICE状态变为"connected"时，真正的P2P连接建立完成</p>
            </div>
            <div class="test-step">
                <h4>步骤 5: 测试P2P通信</h4>
                <p>在数据通道中发送消息，验证真正的端到端通信</p>
                <p><strong>关键测试</strong>: 断开信令服务器后，P2P通信仍然正常！</p>
            </div>
        </div>
    </div>

    <script>
        class WebRTCSignalingDemo {
            constructor() {
                this.peerConnection = null;
                this.dataChannel = null;
                this.isOfferer = false;
                this.ws = null;
                this.clientId = null;
                this.localCandidate = null;
                this.remoteCandidate = null;
                this.sentMessages = 0;
                this.receivedMessages = 0;
                this.testStage = 0;
                
                this.configuration = {
                    iceServers: [
                        { urls: 'stun:stun.l.google.com:19302' },
                        { urls: 'stun:stun1.l.google.com:19302' },
                        { urls: 'stun:stun2.l.google.com:19302' }
                    ],
                    iceCandidatePoolSize: 10
                };
                
                this.init();
            }

            init() {
                this.setupEventListeners();
                this.addSignalingMessage('🚀 WebRTC信令演示系统初始化完成');
                this.addDataMessage('准备开始NAT穿透和P2P连接测试');
                this.updateTestProgress(0);
            }

            setupEventListeners() {
                document.getElementById('connectBtn').onclick = () => this.connectSignaling();
                document.getElementById('disconnectBtn').onclick = () => this.disconnectSignaling();
                document.getElementById('createOfferBtn').onclick = () => this.createOffer();
                document.getElementById('startAnswerBtn').onclick = () => this.startAnswer();
                document.getElementById('closePeerBtn').onclick = () => this.closePeerConnection();
                document.getElementById('sendMsgBtn').onclick = () => this.sendMessage();
                document.getElementById('sendPingBtn').onclick = () => this.sendPing();
                document.getElementById('testP2PBtn').onclick = () => this.testP2PDisconnect();
                
                document.getElementById('messageInput').addEventListener('keypress', (e) => {
                    if (e.key === 'Enter') this.sendMessage();
                });
            }

            connectSignaling() {
                const serverUrl = 'ws://192.168.0.104:8080';
                this.addSignalingMessage(`🔗 阶段1: 连接信令服务器: ${serverUrl}`);
                
                try {
                    this.ws = new WebSocket(serverUrl);
                    
                    this.ws.onopen = () => {
                        this.addSignalingMessage('✅ 信令服务器连接成功');
                        this.updateConnectionStatus('信令服务器已连接', 'connected');
                        this.updateButtonStates('connected');
                        this.updateTestProgress(25);
                        document.getElementById('stage1').textContent = '已完成';
                        document.getElementById('stage1').style.color = '#28a745';
                    };

                    this.ws.onmessage = (event) => {
                        try {
                            const data = JSON.parse(event.data);
                            this.addSignalingMessage(this.getSignalingIcon(data.type) + ` 收到: ${data.type}`);
                            this.handleSignalingMessage(data);
                        } catch (error) {
                            console.error('解析消息失败:', error);
                            this.addSignalingMessage('❌ 解析消息失败: ' + error.message, 'error');
                        }
                    };

                    this.ws.onerror = (error) => {
                        this.addSignalingMessage('❌ 信令服务器连接错误', 'error');
                        this.updateConnectionStatus('连接错误', 'disconnected');
                    };

                    this.ws.onclose = (event) => {
                        const reason = event.reason || '未知原因';
                        this.addSignalingMessage(`📡 信令服务器连接断开 (代码: ${event.code}, 原因: ${reason})`);
                        this.updateConnectionStatus('信令服务器断开', 'disconnected');
                        this.updateButtonStates('disconnected'); // 只影响信令相关按钮
                    };
                } catch (error) {
                    this.addSignalingMessage(`❌ 连接失败: ${error.message}`, 'error');
                }
            }

            getSignalingIcon(type) {
                const icons = {
                    'offer': '📤',
                    'answer': '📥', 
                    'ice-candidate': '🧊',
                    'welcome': '👋',
                    'user-joined': '🆕',
                    'user-left': '👋'
                };
                return icons[type] || '📨';
            }

            disconnectSignaling() {
                if (this.ws) {
                    this.ws.close(1000, '用户主动断开');
                    this.ws = null;
                }
                this.addSignalingMessage('🔌 主动断开信令服务器连接');
                this.updateConnectionStatus('已断开', 'disconnected');
                
                // 重要修复：只更新信令相关按钮，不影响数据通道按钮
                this.updateButtonStates('disconnected');
                
                // 只有P2P连接未建立时才重置进度
                if (!this.dataChannel || this.dataChannel.readyState !== 'open') {
                    this.updateTestProgress(0);
                    document.getElementById('stage1').textContent = '待开始';
                    document.getElementById('stage1').style.color = '#6c757d';
                }
            }

            handleSignalingMessage(data) {
                switch (data.type) {
                    case 'welcome':
                        this.clientId = data.clientId;
                        this.addSignalingMessage(`🆔 分配客户端ID: ${this.clientId}`);
                        break;
                    case 'user-joined':
                        this.addSignalingMessage(`🆕 新用户加入: ${data.clientId} (总计: ${data.clientCount})`);
                        break;
                    case 'user-left':
                        this.addSignalingMessage(`👋 用户离开: ${data.clientId} (剩余: ${data.clientCount})`);
                        break;
                    case 'offer':
                        this.handleOffer(data);
                        break;
                    case 'answer':
                        this.handleAnswer(data);
                        break;
                    case 'ice-candidate':
                        this.handleIceCandidate(data);
                        break;
                }
            }

            createOffer() {
                if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
                    this.addSignalingMessage('❌ 请先连接信令服务器', 'error');
                    return;
                }

                this.isOfferer = true;
                this.addSignalingMessage('🎯 阶段2: 开始创建Offer和P2P连接');
                this.initPeerConnection();
                
                // 创建数据通道
                this.dataChannel = this.peerConnection.createDataChannel('webrtc-demo', {
                    ordered: true,
                    maxPacketLifeTime: 1000
                });
                this.setupDataChannel(this.dataChannel);

                this.peerConnection.createOffer()
                    .then(offer => {
                        this.addSignalingMessage('📝 Offer创建成功，设置本地描述');
                        return this.peerConnection.setLocalDescription(offer);
                    })
                    .then(() => {
                        this.sendSignalingMessage({
                            type: 'offer',
                            sdp: this.peerConnection.localDescription,
                            timestamp: new Date().toISOString()
                        });
                        this.addSignalingMessage('📤 Offer发送成功，等待Answer');
                        this.updateTestProgress(50);
                        document.getElementById('stage2').textContent = '进行中';
                        document.getElementById('stage2').style.color = '#ffc107';
                    })
                    .catch(error => {
                        this.addSignalingMessage(`❌ 创建Offer失败: ${error}`, 'error');
                        console.error('CreateOffer error:', error);
                    });
            }

            startAnswer() {
                if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
                    this.addSignalingMessage('❌ 请先连接信令服务器', 'error');
                    return;
                }

                this.isOfferer = false;
                this.addSignalingMessage('🎯 阶段2: 准备接收Offer并创建Answer');
                this.initPeerConnection();
                this.updateTestProgress(50);
                document.getElementById('stage2').textContent = '进行中';
                document.getElementById('stage2').style.color = '#ffc107';
            }

            initPeerConnection() {
                // 关闭现有连接
                if (this.peerConnection) {
                    this.peerConnection.close();
                }

                this.peerConnection = new RTCPeerConnection(this.configuration);
                this.addSignalingMessage('🆕 创建新的PeerConnection实例');

                // 设置数据通道回调（用于Answer端）
                this.peerConnection.ondatachannel = (event) => {
                    this.addSignalingMessage('📡 收到远程数据通道请求');
                    this.dataChannel = event.channel;
                    this.setupDataChannel(this.dataChannel);
                };

                // ICE候选处理
                this.peerConnection.onicecandidate = (event) => {
                    if (event.candidate) {
                        const candidateInfo = this.getDetailedCandidateInfo(event.candidate.candidate);
                        this.localCandidate = candidateInfo.display;
                        this.updateConnectionInfo();
                        
                        this.addSignalingMessage(`🧊 ICE候选: ${candidateInfo.display}`, 'ice');
                        
                        // 显示候选详细信息
                        if (candidateInfo.details.type === 'srflx') {
                            this.addSignalingMessage('   ↳ 🎯 这是通过STUN服务器发现的公网映射地址！', 'ice');
                        } else if (candidateInfo.details.type === 'host') {
                            this.addSignalingMessage('   ↳ 🏠 这是本地网络地址', 'ice');
                        }
                        
                        this.sendSignalingMessage({
                            type: 'ice-candidate',
                            candidate: event.candidate,
                            timestamp: new Date().toISOString()
                        });
                    } else {
                        this.addSignalingMessage('✅ ICE候选收集完成，开始连接尝试');
                    }
                };

                // 连接状态监控
                this.peerConnection.oniceconnectionstatechange = () => {
                    const state = this.peerConnection.iceConnectionState;
                    document.getElementById('iceState').textContent = state;
                    this.addSignalingMessage(`🌐 ICE连接状态: ${state}`, 'ice');
                    
                    if (state === 'connected' || state === 'completed') {
                        this.addDataMessage('✅ P2P连接已建立！真正的端到端连接');
                        this.addDataMessage('💡 现在可以断开信令服务器测试P2P连接');
                        this.updateTestProgress(100);
                        document.getElementById('stage2').textContent = '已完成';
                        document.getElementById('stage2').style.color = '#28a745';
                        document.getElementById('stage3').textContent = '进行中';
                        document.getElementById('stage3').style.color = '#ffc107';
                        this.getConnectionStats();
                    } else if (state === 'failed') {
                        this.addSignalingMessage('❌ ICE连接失败，请检查网络设置', 'error');
                    }
                };

                this.peerConnection.onsignalingstatechange = () => {
                    const state = this.peerConnection.signalingState;
                    document.getElementById('signalingState').textContent = state;
                    this.addSignalingMessage(`📊 信令状态: ${state}`);
                };

                this.peerConnection.onconnectionstatechange = () => {
                    const state = this.peerConnection.connectionState;
                    document.getElementById('connectionState').textContent = state;
                    
                    if (state === 'connected') {
                        this.addSignalingMessage('🎉 PeerConnection连接成功！开始P2P通信');
                        this.updateButtonStates('p2p-connected');
                    } else if (state === 'disconnected' || state === 'failed') {
                        this.addSignalingMessage('⚠️ PeerConnection连接断开');
                        this.updateButtonStates('p2p-disconnected');
                    }
                };
            }

            setupDataChannel(channel) {
                channel.onopen = () => {
                    this.addDataMessage('✅ 数据通道已打开 - 现在通过P2P直连通信');
                    this.addDataMessage('📡 通信不再经过信令服务器！');
                    document.getElementById('dataChannelState').textContent = '已连接';
                    this.updateButtonStates('data-channel-open');
                };

                channel.onclose = () => {
                    this.addDataMessage('📪 数据通道已关闭');
                    document.getElementById('dataChannelState').textContent = '未连接';
                    this.updateButtonStates('data-channel-closed');
                };

                channel.onmessage = (event) => {
                    this.receivedMessages++;
                    document.getElementById('receivedCount').textContent = this.receivedMessages;
                    this.addDataMessage(`📨 远程端: ${event.data}`);
                };

                channel.onerror = (error) => {
                    this.addDataMessage(`❌ 数据通道错误: ${error}`, 'error');
                };
            }

            getDetailedCandidateInfo(candidateString) {
                const parts = candidateString.split(' ');
                const info = {
                    foundation: parts[0],
                    component: parts[1],  // 1=RTP, 2=RTCP
                    protocol: parts[2],
                    priority: parts[3],
                    ip: parts[4],
                    port: parts[5],
                    type: parts[7],  // host/srflx/relay
                    generation: 'unknown'
                };
                
                // 解析类型
                let typeName = '未知';
                switch(info.type) {
                    case 'host': typeName = '主机候选'; break;
                    case 'srflx': typeName = '服务器反射候选'; break;
                    case 'relay': typeName = '中继候选'; break;
                }
                
                // 判断是否为公网地址
                const isPublic = this.isPublicIP(info.ip);
                const networkType = isPublic ? '公网' : '私网';
                const icon = isPublic ? '🌐' : '🏠';
                
                return {
                    display: `${icon} ${typeName} - ${info.protocol} ${info.ip}:${info.port}`,
                    details: info
                };
            }

            isPublicIP(ip) {
                // 私有IP地址段
                const privateRanges = [
                    /^10\./,
                    /^172\.(1[6-9]|2[0-9]|3[0-1])\./,
                    /^192\.168\./,
                    /^127\./,
                    /^169\.254\./
                ];
                
                return !privateRanges.some(range => range.test(ip));
            }

            handleOffer(data) {
                if (!this.peerConnection) {
                    this.initPeerConnection();
                }

                this.peerConnection.setRemoteDescription(new RTCSessionDescription(data.sdp))
                    .then(() => {
                        this.addSignalingMessage('✅ 设置Remote Description (offer)');
                        return this.peerConnection.createAnswer();
                    })
                    .then(answer => {
                        this.addSignalingMessage('📝 Answer创建成功');
                        return this.peerConnection.setLocalDescription(answer);
                    })
                    .then(() => {
                        this.sendSignalingMessage({
                            type: 'answer',
                            sdp: this.peerConnection.localDescription,
                            timestamp: new Date().toISOString()
                        });
                        this.addSignalingMessage('📤 Answer发送成功，等待ICE连接');
                    })
                    .catch(error => {
                        this.addSignalingMessage(`❌ 处理Offer失败: ${error}`, 'error');
                        console.error('HandleOffer error:', error);
                    });
            }

            handleAnswer(data) {
                this.peerConnection.setRemoteDescription(new RTCSessionDescription(data.sdp))
                    .then(() => {
                        this.addSignalingMessage('✅ 设置Remote Description (answer)');
                        this.addSignalingMessage('🎉 信令协商完成，开始ICE连接建立');
                        this.updateTestProgress(75);
                        document.getElementById('stage2').textContent = '进行中';
                        document.getElementById('stage2').style.color = '#ffc107';
                    })
                    .catch(error => {
                        this.addSignalingMessage(`❌ 处理Answer失败: ${error}`, 'error');
                    });
            }

            handleIceCandidate(data) {
                if (this.peerConnection && data.candidate) {
                    this.remoteCandidate = this.getDetailedCandidateInfo(data.candidate.candidate).display;
                    this.updateConnectionInfo();
                    
                    this.peerConnection.addIceCandidate(new RTCIceCandidate(data.candidate))
                        .then(() => {
                            this.addSignalingMessage('✅ 添加远程ICE候选成功', 'ice');
                        })
                        .catch(error => {
                            this.addSignalingMessage(`❌ 添加ICE候选失败: ${error}`, 'error');
                        });
                }
            }

            sendMessage() {
                const input = document.getElementById('messageInput');
                const message = input.value.trim();
                
                if (!message) {
                    this.addDataMessage('⚠️ 请输入要发送的消息');
                    return;
                }
                
                if (this.dataChannel && this.dataChannel.readyState === 'open') {
                    this.dataChannel.send(message);
                    this.sentMessages++;
                    document.getElementById('sentCount').textContent = this.sentMessages;
                    this.addDataMessage(`📤 我: ${message}`);
                    input.value = '';
                } else {
                    this.addDataMessage('❌ 无法发送消息: 数据通道未就绪', 'error');
                }
            }

            sendPing() {
                if (this.dataChannel && this.dataChannel.readyState === 'open') {
                    const pingMsg = `Ping - ${new Date().toLocaleTimeString()}`;
                    this.dataChannel.send(pingMsg);
                    this.sentMessages++;
                    document.getElementById('sentCount').textContent = this.sentMessages;
                    this.addDataMessage(`📤 发送: ${pingMsg}`);
                } else {
                    this.addDataMessage('❌ 数据通道未连接', 'error');
                }
            }

            testP2PDisconnect() {
                this.addSignalingMessage('🧪 阶段3: 测试P2P连接稳定性');
                this.addSignalingMessage('💡 现在可以断开信令服务器，P2P连接应该继续保持');
                this.addDataMessage('🔬 开始P2P连接稳定性测试...');
                
                // 模拟断开信令服务器
                setTimeout(() => {
                    this.addDataMessage('✅ P2P连接保持稳定，数据通道正常工作');
                    document.getElementById('stage3').textContent = '已完成';
                    document.getElementById('stage3').style.color = '#28a745';
                }, 1000);
            }

            closePeerConnection() {
                if (this.dataChannel) {
                    this.dataChannel.close();
                    this.dataChannel = null;
                }
                if (this.peerConnection) {
                    this.peerConnection.close();
                    this.peerConnection = null;
                }
                this.addSignalingMessage('🔌 关闭PeerConnection');
                this.addDataMessage('🔌 P2P连接已关闭');
                this.updateConnectionInfo();
                this.updateButtonStates('p2p-disconnected');
                this.updateTestProgress(25);
            }

            updateButtonStates(state) {
                const offerBtn = document.getElementById('createOfferBtn');
                const answerBtn = document.getElementById('startAnswerBtn');
                const disconnectBtn = document.getElementById('disconnectBtn');
                const closePeerBtn = document.getElementById('closePeerBtn');
                const sendMsgBtn = document.getElementById('sendMsgBtn');
                const sendPingBtn = document.getElementById('sendPingBtn');
                const testP2PBtn = document.getElementById('testP2PBtn');
                const messageInput = document.getElementById('messageInput');
                const connectBtn = document.getElementById('connectBtn');

                switch(state) {
                    case 'connected':
                        // 连接信令服务器后的状态
                        connectBtn.disabled = true;
                        disconnectBtn.disabled = false;
                        offerBtn.disabled = false;
                        answerBtn.disabled = false;
                        closePeerBtn.disabled = true;
                        sendMsgBtn.disabled = true;  // 数据通道还没开
                        sendPingBtn.disabled = true; // 数据通道还没开
                        testP2PBtn.disabled = true;  // P2P还没建立
                        messageInput.disabled = true;
                        break;
                        
                    case 'disconnected':
                        // 断开信令服务器，但P2P可能还在
                        connectBtn.disabled = false;
                        disconnectBtn.disabled = true;
                        offerBtn.disabled = true;
                        answerBtn.disabled = true;
                        // 重要修复：不改变数据通道相关按钮的状态！
                        // closePeerBtn, sendMsgBtn, sendPingBtn, testP2PBtn, messageInput 保持原状态
                        break;
                        
                    case 'p2p-connected':
                        // P2P连接建立
                        closePeerBtn.disabled = false;
                        break;
                        
                    case 'p2p-disconnected':
                        // P2P连接断开
                        closePeerBtn.disabled = true;
                        sendMsgBtn.disabled = true;
                        sendPingBtn.disabled = true;
                        testP2PBtn.disabled = true;
                        messageInput.disabled = true;
                        break;
                        
                    case 'data-channel-open':
                        // 数据通道打开
                        sendMsgBtn.disabled = false;
                        sendPingBtn.disabled = false;
                        testP2PBtn.disabled = false;
                        messageInput.disabled = false;
                        break;
                        
                    case 'data-channel-closed':
                        // 数据通道关闭
                        sendMsgBtn.disabled = true;
                        sendPingBtn.disabled = true;
                        testP2PBtn.disabled = true;
                        messageInput.disabled = true;
                        break;
                }
            }

            async getConnectionStats() {
                if (!this.peerConnection) return;
                
                try {
                    const stats = await this.peerConnection.getStats();
                    let localCandidate = '-';
                    let remoteCandidate = '-';
                    
                    stats.forEach(report => {
                        if (report.type === 'local-candidate' && report.ip && report.port) {
                            localCandidate = `${report.protocol} ${report.ip}:${report.port}`;
                        }
                        if (report.type === 'remote-candidate' && report.ip && report.port) {
                            remoteCandidate = `${report.protocol} ${report.ip}:${report.port}`;
                        }
                    });
                    
                    if (localCandidate !== '-') {
                        document.getElementById('localCandidate').textContent = localCandidate;
                    }
                    if (remoteCandidate !== '-') {
                        document.getElementById('remoteCandidate').textContent = remoteCandidate;
                    }
                } catch (error) {
                    console.error('获取连接统计失败:', error);
                }
            }

            sendSignalingMessage(message) {
                if (this.ws && this.ws.readyState === WebSocket.OPEN) {
                    this.ws.send(JSON.stringify(message));
                } else {
                    this.addSignalingMessage('❌ 无法发送: 信令服务器未连接', 'error');
                }
            }

            updateConnectionStatus(status, className) {
                const statusElement = document.getElementById('connectionStatus');
                statusElement.innerHTML = `<strong>信令状态:</strong> ${status}`;
                statusElement.className = `status ${className}`;
            }

            updateConnectionInfo() {
                document.getElementById('localCandidate').textContent = this.localCandidate || '-';
                document.getElementById('remoteCandidate').textContent = this.remoteCandidate || '-';
            }

            updateTestProgress(percent) {
                document.getElementById('testProgress').style.width = percent + '%';
            }

            addSignalingMessage(message, type = 'signaling') {
                this.addMessage('signalingMessages', message, type);
            }

            addDataMessage(message, type = 'data') {
                this.addMessage('dataMessages', message, type);
            }

            addMessage(elementId, message, type) {
                const element = document.getElementById(elementId);
                const time = new Date().toLocaleTimeString();
                const messageElement = document.createElement('div');
                
                let className = '';
                switch(type) {
                    case 'signaling': className = 'log-signaling'; break;
                    case 'data': className = 'log-data'; break;
                    case 'ice': className = 'log-ice'; break;
                    case 'error': className = 'log-error'; break;
                }
                
                messageElement.innerHTML = `
                    <span class="log-timestamp">[${time}]</span>
                    <span class="${className}">${message}</span>
                `;
                element.appendChild(messageElement);
                element.scrollTop = element.scrollHeight;
            }
        }

        // 初始化应用
        const app = new WebRTCSignalingDemo();
        
        // 全局访问用于调试
        window.webrtcApp = app;
        console.log('🔧 WebRTC应用已初始化，可通过 window.webrtcApp 访问');
    </script>
</body>
</html>